/**
 * 
 */
package de.guidoludwig.jtm.hibernate;

import java.util.Map;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.hibernate.search.bridge.LuceneOptions;
import org.hibernate.search.bridge.ParameterizedBridge;
import org.hibernate.search.bridge.TwoWayFieldBridge;

/**
 * @author guido
 *
 */
public class EnumerationBridge implements TwoWayFieldBridge,
		ParameterizedBridge {
	
	private static final String PROPERTY_ENUM_TYPE = "enumType";
	private static final String PROPERTY_VALUE = "value";
	
	@SuppressWarnings("rawtypes")
	private Class<Enum> enumType;
	private java.lang.reflect.Field reflectionField;

	@Override
	public void set(String name, Object value, Document doc, LuceneOptions opts) {
		if (value == null) {
			return;
		}
		String indexString = objectToString(value);
		Field f = new Field(name, indexString, opts.getStore(), opts.getIndex());
		if (opts.getBoost() != 0) {
			f.setBoost(opts.getBoost());
		}
		doc.add(f);
		if (reflectionField != null) {
			Object fieldValue = getFieldValue(value);
			Field f2 = new Field(name + "." + PROPERTY_VALUE, fieldValue.toString(), Field.Store.NO, opts.getIndex());
			doc.add(f2);
		}
	}

	private Object getFieldValue(Object value) {
		reflectionField.setAccessible(true);
		try {
			return reflectionField.equals(value);
		} catch (Exception e) {
			throw new IllegalStateException();
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public void setParameterValues(Map<String, String> m) {
		String className = (String)m.get(PROPERTY_ENUM_TYPE);
		try {
			enumType = (Class<Enum>) Class.forName(className);
		} catch (Exception e) {
			throw new IllegalStateException("wrong annotation");
		}
		String value = (String)m.get(PROPERTY_VALUE);
		if (value != null) {
			try {
				reflectionField = enumType.getDeclaredField(value);
			} catch (NoSuchFieldException e) {
				throw new IllegalStateException("no such field");
			}
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public Object get(String s, Document d) {
		Field f = d.getField(s);
		if (f == null) {
			return null;
		}
		return Enum.valueOf(enumType, f.stringValue().toUpperCase());
	}

	@Override
	public String objectToString(Object o) {
		if (!(o instanceof Enum<?>)) {
			return null;
		}
		return o == null ? null : ((Enum<?>)o).name().toLowerCase();
	}

}
